// 数组（固定长度）

use std::io;

fn main() {
    println!("Hello, world!");
}

// 声明
#[test]
fn learn01() {
    // 1. 声明数组
    // 数组元素类型一致，长度固定，顺序排列
    let _arr = [1, 2, 3, 4];

    // 2. 声明数组同时声明类型，如果声明类型，则必须同时声明长度
    let _arr: [i64; 5] = [1, 2, 3, 4, 5];

    // 2.1 可以仅声明长度，但是不能仅声明类型
    let _arr: [_; 5] = [1, 2, 3, 4, 5];

    // 3. 与 C 不同, Rust 声明数组长度可以使用常量，注意是常量，且类型必须是 usize
    const LEN: usize = 3;
    let _arr: [&str; LEN] = ["张三", "李四", "赵六"];

    // 4. 数组中可以声明 String 类型
    let s1 = String::from("张三");
    let s2 = String::from("李四");
    let _arr = [s1, s2];

    // 5. 声明重复出现的元素
    // 此处将生成的数组包含 5 个 3，这就类似声明类型的方式 [类型; 长度]
    let _arr = [3u64; 5];

    println!("{:?}", _arr);
}

// 取值
#[test]
fn learn02() {
    let arr = [1, 2, 3, 4, 5];

    // 1. 按下标获取元素
    let num = arr[1];

    print!("{}\n", num);

    // 读取输入的数字并获取对应下标的元素
    let mut index = String::new();
    // 读取控制台的输出
    io::stdin()
        .read_line(&mut index)
        .expect("Failed to read line");

    let index: usize = index
        .trim()
        .parse()
        .expect("Index entered was not a number");

    // Rust 会检查数组下标是否越界，当下标越界时，Rust 会抛出 error 并阻断程序运行
    // 这与 Java 类似，而与 C 有别
    let num = arr[index];

    print!("{}\n", num);
}

// 取值2
#[test]
fn learn0202() {
    let names = [String::from("Sunfei"), "Sunface".to_string()];
    
    // `get` 返回 `Option<T>` 类型，因此它的使用非常安全
    let name0 = names.get(2);

    println!("{:?}", name0);
}


// 数组元素为非基础类型时
#[test]
fn learn03() {
    // 当使用重复声明元素的方式声明非基础类型元素时，编译器会报错：
    // the `Copy` trait is required because this value will be copied for each element of the array
    // let _arr = [String::from("你好"); 3];

    // 这是因为重复声明元素的简写方式底层就是在 Copy 元素，但是复杂类型没有深拷贝
    // 为了解决这个问题，有两种方式
    // 1. 手动声明重复元素，冗长且难看
    let _arr = [
        String::from("你好"),
        String::from("你好"),
        String::from("你好"),
    ];

    // 2. 调用 std::array::from_fn
    // 此方法必须声明变量的长度
    let _arr: [String; 3] = std::array::from_fn(|_i| String::from("rust is good!"));
}

// 数组切片
#[test]
fn learn04() {
    /*
        - 切片的长度可以与数组不同，并不是固定的，而是取决于你使用时指定的起始和结束位置
        - 创建切片的代价非常小，因为切片只是针对底层数组的一个引用
        - 切片类型[T]拥有不固定的大小，而切片引用类型&[T]则具有固定的大小，因为 Rust 很多时候都需要固定大小数据类型，因此&[T]更有用,&str字符串切片也同理
    */
    let a: [i32; 5] = [1, 2, 3, 4, 5];

    let slice: &[i32] = &a[1..3];

    assert_eq!(slice, &[2, 3]);
}

/*
总结：
    - 数组类型容易跟数组切片混淆，[T;n]描述了一个数组的类型，而[T]描述了切片的类型， 因为切片是运行期的数据结构，它的长度无法在编译期得知，因此不能用[T;n]的形式去描述
    - [u8; 3]和[u8; 4]是不同的类型，数组的长度也是类型的一部分
    - 在实际开发中，使用最多的是数组切片[T]，我们往往通过引用的方式去使用&[T]，因为后者有固定的类型大小
*/

